use core::str;

use bytes::{Buf, Bytes};

///为查找有效字符位置的返回结果
#[derive(Debug)]
pub struct Place {
	///有效的开始位置
	pub start: usize,
	///有效的结束位置
	pub end: usize,
	///查找到的结束符(包括)的位置
	pub stop: usize,
}

///从头开始并且直到停止符,,并排除无用字符
/// 如果没有碰到指定的结束符或行结束符,则到最后位置
/// 减少buf的空间,,,返回指定相关.并会去掉分隔符和无用的字符
#[inline]
pub fn split_on(buf: &mut Bytes, end_byte: u8) -> Bytes {
	let place = from_line_begin_to_stop_trim(buf, end_byte);

	let mut r = buf.split_to(place.stop);
	r.truncate(place.end + 1);
	r.advance(place.start);

	if !buf.is_empty() {
		buf.advance(1);
	}

	r
}

///从头开始并且直到停止符,,并排除无用字符
/// 此方法会先去除掉开始可能存在的无用字符.但是不会跳过换行符
#[inline]
pub fn trim_split_on(buf: &mut Bytes, end_byte: u8) -> Bytes {
	//跳过前面不需要的部分
	for i in 0..buf.len() {
		if ![b' ', b'\t'].contains(&buf[i]) {
			buf.advance(i);
			break;
		}
	}

	split_on(buf, end_byte)
}

///从头开始并且直到指定的停止符或行结束符,,并排除无用字符
/// 如果没有碰到指定的结束符或行结束符,则到最后位置
/// 返回的stop位置指向要分离的下一个,即可直接使用 split_to进行分离
/// 会根据ascii去除无用字符.
#[inline]
pub fn from_line_begin_to_stop_trim(buf: &[u8], end_byte: u8) -> Place {
	if buf.is_empty() {
		return Place {
			start: 0,
			end: 0,
			stop: 0,
		};
	}

	let mut start = 0usize;
	let mut end;
	let mut is_start = true;
	let mut stop = buf.len() - 1;

	for i in 0..buf.len() {
		let val = buf[i];
		if val == end_byte || val == b'\r' || val == b'\n' {
			//如果是"\r",判断后续是否"\n"..是则多向下指一个
			if val == b'\r' && i < buf.len() - 1 && buf[i + 1] == b'\n' {
				stop = i;
			} else {
				stop = i.saturating_sub(1);
			}

			if is_start {
				start = i;
			}

			break;
		}

		//这里是还没有找到第一个有效字符
		if is_start && !val.is_ascii_whitespace() {
			start = i;
			is_start = false;
		}
	}

	//让stop一直等于下一个.包括最后那个
	stop += 1;
	end = start;
	for i in (start..stop).rev() {
		if is_visible_ascii(buf[i]) {
			end = i;
			break;
		}
	}

	Place { start, end, stop }
}

///判断一个u8是否可见的ascii字符的内码
#[inline]
pub fn is_visible_ascii(b: u8) -> bool {
	match b {
		0x21..=0x7e => true,
		_ => false,
	}
}

#[inline]
///将一个&[u8]内容转为str.因为内部使用非安全代码,做一个封装
pub fn str_from_utf8_bytes(buf: &[u8]) -> &str {
	unsafe { str::from_utf8_unchecked(buf) }
}
